/*
	Copyright (c) 2012 by karmaleaving@qq.com. All rights RESERVED.
*/

#ifndef __SOCKET_H_VERSION_KL__
#define __SOCKET_H_VERSION_KL__

#include "config.h"
#ifdef _KL_UNIX_
#include <sys/socket.h>
#else
#include <windows.h>
#pragma comment(lib, "WS2_32.LIB")
#endif // _KL_UNIX_

KL_BEGIN
template <class Int>
inline Int host2net(Int host)
{
	Int net;
	u1_t *dest = (u1_t*)&net;
	for(size_t i = 1; i <= sizeof(Int); ++i)
	{
		dest[sizeof(Int) - i] = host & Value::U1_MAX;
		host >>= Value::Oct;
	}
	return net;
}
template <class Int>
inline Int net2host(Int net)
{
	Int host = KL_NULL;
	u1_t *source = (u1_t*)&net;
	for(size_t i = 0; i < sizeof(Int); ++i)
	{
		host = (host << Value::Oct) + source[i];
	}
	return host;
}
struct MsgFlag	
{	// for <recv> and <send>
	enum _ { Normal = 0, OutBand = MSG_OOB }; 
};
struct MsgRes
{	// for <recv> and <send>.
	typedef int _;
	_ bytes;
	MsgRes(_ bytes = 0) : bytes(bytes)
	{
	}
	operator _ ()
	{
		return bytes;
	}
	int size() const
	{
		if(bytes < INT_OK) return 0;
		return bytes;
	}
	int error() const
	{
		if(bytes < INT_OK) return bytes;
		return INT_OK;
	}
};
struct AF 
{ // address family
	enum _ { Default = 0, Unix = AF_UNIX, Inet = AF_INET}; 
};
struct SockType 
{	// for <open>
	enum _ { Stream = SOCK_STREAM, Dgram = SOCK_DGRAM,	Raw = SOCK_RAW};
};
struct ProType 
{	// for <open>
	enum _ { Ip = IPPROTO_IP, Raw = IPPROTO_RAW }; 
};
struct Address
{
	u4_t ip;
	u2_t port;
	i2_t family;
	Address()
	{}
	Address(Address const &addr)
		: ip(addr.ip), port(addr.port), family(addr.family)
	{
	}
	Address(u2_t host_port, 
		u1_t ip0, u1_t ip1, u1_t ip2, u1_t ip3,
		AF::_ af = AF::Inet)
		: port(host2net(host_port))
		, family(af)
	{
		((u1_t *)(&ip))[0] = ip0;
		((u1_t *)(&ip))[1] = ip1;
		((u1_t *)(&ip))[2] = ip2;
		((u1_t *)(&ip))[3] = ip3;
	}
	Address(u2_t host_port, const char *addr, AF::_ af = AF::Inet)
		: port(host2net(host_port))
		, ip(inet_addr(addr))	
		, family(af)
	{
	}
	Address(u4_t net_ip, u2_t net_port)
		: ip(net_ip), port(net_port)
	{
	}
};
struct HowShut {	// for <shutdown>
	enum _ { NoRecv = 0, NoSend = 1, Close = 2 }; 
};	
class Socket
{
public:
	// STATIC S {
#ifdef _KL_UNIX_
#else
	typedef SOCKET fd_type;
	static const fd_type InvalidSocket = INVALID_SOCKET;
	static void Startup(WORD version = 0x0202)
	{
		WSADATA wsadata;
		WSAStartup(version, &wsadata);
	}
	static void Cleanup()
	{
		WSACleanup();
	}
#endif // _KL_UNIX_
	static int Send(fd_type s, const char *buf, int len, MsgFlag::_ flag)
	{
		return ::send(s, buf, len, flag);
	}
	static int Recv(fd_type s, char *buf, int len, MsgFlag::_ flag)
	{
		return ::recv(s, buf, len, flag);
	}
	static fd_type Open(AF::_ af, SockType::_ st, ProType::_ pro)
	{
		return ::socket(af, st, pro);
	}
	static int Close(fd_type s)
	{
#ifdef _KL_UNIX_
		return ::close(s);
#else
		return ::closesocket(s);
#endif
	}
	static int Connect(fd_type s, Address const &to)
	{
		struct sockaddr_in addr;
		addr.sin_family = to.family;
		addr.sin_port = to.port;
		addr.sin_addr.s_addr = to.ip;
		::memset(&(addr.sin_zero), 0, sizeof(addr.sin_zero));
		return ::connect(s, (sockaddr*)&addr, sizeof(addr));
	}
	static int Shutdown(fd_type s, HowShut::_ how)
	{
		return ::shutdown(s, how);
	}
	static int Bind(fd_type s, Address const &to)
	{
		struct sockaddr_in addr;
		addr.sin_family = to.family;
		addr.sin_port = to.port;
		addr.sin_addr.s_addr = to.ip;
		::memset(&(addr.sin_zero), 0, sizeof(addr.sin_zero));
		return ::bind(s, (sockaddr*)&addr, sizeof(addr));
	}
	static int Listen(fd_type s, int back)
	{
		return ::listen(s, back);
	}
	static fd_type Accept(fd_type listened)
	{
		struct sockaddr addr;
		//::memset(&(addr.sin_zero), 0, sizeof(addr.sin_zero));
		int size = sizeof(addr);
		return ::accept(listened, &addr, &size);
	}
	static int Not_wait(fd_type s, unsigned long value)
	{
		return ::ioctlsocket(s, FIONBIO, &value);
	}
	static unsigned long Try_read(fd_type s)
	{
		unsigned long arg(0);
		ioctlsocket(s, FIONREAD, &arg);
		return arg;
	}
	// STATIC S }
protected:
	fd_type _fd;
public:
	Socket(fd_type fd = InvalidSocket) : _fd(fd)
	{}
	MsgRes recv(char *buf, int max_size)
	{
		return Recv(_fd, buf, max_size, MsgFlag::Normal);
	}
	MsgRes send(const char *buf, int max_size)
	{
		return Send(_fd, buf, max_size, MsgFlag::Normal);
	}
	MsgRes recv_out_band(char *buf, int max_size)
	{
		return Recv(_fd, buf, max_size, MsgFlag::OutBand);
	}
	MsgRes recv_out_band(const char *buf, int max_size)
	{
		return Send(_fd, buf, max_size, MsgFlag::OutBand);
	}
	bool not_wait(bool flag) const
	{
		return Not_wait(_fd, flag) == INT_OK;
	}
	size_t try_read() const
	{
		return Try_read(_fd);
	}
	bool is_connect() const
	{
		if(Send(_fd, "", 0, MsgFlag::Normal) == 0) return false;
		return true;
	}
	bool close()
	{
		if(valid() && Close(_fd) == INT_OK)
		{
			_fd = InvalidSocket;
			return true;
		}
		return false;
	}
	bool shutdown(HowShut::_ how)
	{
		if(valid()) return Shutdown(_fd, how) == INT_OK;
		return false;
	}
	fd_type get() const
	{
		return _fd;
	}
	void set(fd_type fd)
	{
		_fd = fd;
	}
	bool valid() const
	{
		return _fd != InvalidSocket;
	}
	virtual ~Socket()
	{
	}
};
class Listener : public Socket
{
protected:
	typedef Socket Base;
	bool _bind_flag;
	bool _listen_flag;
public:
	Listener()
		: _bind_flag(false)
		, _listen_flag(false)
	{
	}
	Listener(AF::_ af, SockType::_ st, ProType::_ pro)
		: _bind_flag(false)
		, _listen_flag(false)
	{	// open. call valid() to check whether opened ok.
		this->open(af, st, pro);
	}
	Listener(Address const &local, SockType::_ st, 
		ProType::_ pro, int backlog = 4)
		: _bind_flag(false)
		, _listen_flag(false)
	{	// open and bind and listen.
		this->open((AF::_)local.family, st, pro);
		this->bind(local);
		this->listen(backlog);
	}
	bool close()
	{
		_bind_flag = _listen_flag = false;
		return Base::close();
	}
	bool open(AF::_ af, SockType::_ st, ProType::_ pro)
	{
		if(Base::valid()) return false; // has opened
		Base::set(Base::Open(af, st, pro));
		_bind_flag = _listen_flag = false;
		return Base::valid();
	}
	bool bind(Address const &local)
	{
		if(_bind_flag || !Base::valid()) return false; // has binded.
		_bind_flag = (Base::Bind(Base::get(), local) == INT_OK);
		return _bind_flag;
	}
	bool is_binded() const
	{
		return _bind_flag;
	}
	bool listen(int backlog = 8)
	{
		if(_listen_flag || !Base::valid()) return false; // has listened.
		_listen_flag = (Base::Listen(Base::get(), backlog) == INT_OK);
		if(_listen_flag) _bind_flag = true;
		return _listen_flag;
	}
	bool is_listened() const
	{
		return _listen_flag;
	}
	Socket accept()
	{
		if(!_listen_flag) return Base::InvalidSocket;
		return Base::Accept(Base::get());
	}
};
class Connector : public Socket
{
private:
	typedef Socket Base;
	bool _connect_flag;
public:	
	Connector() : _connect_flag(false)
	{
	}
	Connector(AF::_ af, SockType::_ st, ProType::_ pro)
		: _connect_flag(false)
	{	// open.
		this->open(af, st, pro);
	}
	Connector(Address const &to, SockType::_ st, ProType::_ pro)
		: _connect_flag(false)
	{	// open.
		this->open((AF::_)to.family, st, pro);
		this->connect(to);
	}
	bool open(AF::_ af, SockType::_ st, ProType::_ pro)
	{
		if(Base::valid()) return false; // has opened.
		Base::set(Base::Open(af, st, pro));
		_connect_flag = false;
		return Base::valid();
	}
	bool close()
	{
		_connect_flag = false;
		return Base::close();
	}
	bool connect(Address const &to)
	{
		if(!Base::valid()) return false;
		_connect_flag = (Socket::Connect(_fd, to) == INT_OK);
		return _connect_flag;
	}
};
KL_END
#endif // __SOCKET_H_VERSION_KL__
